'''
Created on 2013/02/26

@author: sakurai
'''

"""
https://github.com/gitlabhq/gitlabhq/blob/master/doc/api/README.md
https://github.com/gitlabhq/gitlabhq/tree/master/lib/api/*.rb

Implementation status:
 - Users
     + GET /users
         Done as ApiClassV3#get_users
     + GET /users/:id
         Done as ApiClassV3#get_single_user 
     + POST /users
         Done as ApiClassV3#create_user 
     + PUT /users/:id
         Done as ApiClassV3#modify_user
         However, my GitLab returns 405 error... 
     + DELETE /users/:id
         Done as ApiClassV3#delete_user
         However, my GitLab returns 405 error... 
     + GET /user
         Done as ApiClassV3#get_current_user 
     + GET /user/keys
         Done as ApiClassV3#get_ssh_keys
         This is just "currently authenticated user's SSH keys"
     + GET /user/keys/:id
         Done as ApiClassV3#get_single_ssh_key
         Maybe works properly, but not tested
     + POST /user/keys
         Done as ApiClassV3#add_ssh_key
         Maybe works properly, but not tested
     + DELETE /user/keys/:id
         Done as ApiClassV3#delete_ssh_key
         Maybe works properly, but not tested
 - Session
     + POST /session
         Done as ApiClassV3#get_session 
 - Projects
     + GET /projects
     + GET /projects/:id
     + POST /projects
     + GET /projects/:id/members
     + GET /projects/:id/members/:user_id
     + POST /projects/:id/members
     + PUT /projects/:id/members/:user_id
     + DELETE /projects/:id/members/:user_id
     + GET /projects/:id/hooks
     + GET /projects/:id/hooks/:hook_id
     + POST /projects/:id/hooks
     + PUT /projects/:id/hooks/:hook_id
     + DELETE /projects/:id/hooks/:hook_id
 - Groups
 - Snippets
 - Repositories
     + GET /projects/:id/repository/branches
     + GET /projects/:id/repository/branches/:branch
     + PUT /projects/:id/repository/branches/:branch/protect
     + PUT /projects/:id/repository/branches/:branch/unprotect
     + GET /projects/:id/repository/tags
     + GET /projects/:id/repository/commits
     + GET /projects/:id/repository/commits/:sha/blob
 - Issues
 - Milestones
 - Notes
"""

import json
from pygitlabapi import *
from pygitlabapi.v3.users import Users
from pygitlabapi.v3.session import Session
from pygitlabapi.v3.groups import Groups
from pygitlabapi.v3.projects import Projects
from pygitlabapi.v3.projects.snippets import Snippets
from pygitlabapi.v3.projects.repository import Repository
from pygitlabapi.v3.projects.issues import Issues
from pygitlabapi.v3.projects.milestones import Milestones
from pygitlabapi.v3.projects.notes import Notes

__all__=["ApiClassV3"]

DEFAULT_NUMBER_OF_ENTITIES_PER_PAGE=20

class ApiClassV3(ApiClassBase):
  def __init__(self, host, port, email=None, password=None, private_token=None, schema="http"):
    ApiClassBase.__init__(self, host, port, email, password, schema)
    self.api_base += "/api/v3"
    if self.email and self.password:
      self.private_token = self.get_session(email, password)["private_token"]
    elif private_token:
      self.private_token = private_token
    else:
      raise InvalidArgumentsException("Either email&password or private_token is required.")

  def _do_GET_with_pagination(self, url, params={}, headers={}, start=1, stop=-1):
    return self._do_request_with_pagination(self._do_GET, url, params, headers, start, stop)

  def _do_DELETE_with_pagination(self, url, params={}, headers={}, start=1, stop=-1):
    return self._do_request_with_pagination(self._do_DELETE, url, params, headers, start, stop)

  def _do_POST_with_pagination(self, url, params={}, headers={}, start=1, stop=-1):
    return self._do_request_with_pagination(self._do_POST, url, params, headers, start, stop)
  
  def _do_PUT_with_pagination(self, url, params={}, headers={}, start=1, stop=-1):
    return self._do_request_with_pagination(self._do_PUT, url, params, headers, start, stop)

  def _do_request_with_pagination(self, req_method, url, params={}, headers={}, start=1, stop=-1):
    """
      req_method: Basically should be _do_GET.
      start: Start page including this number.
      stop: Stop page including this number. -1 means unlimited until all data has been retrieved.
      return: ((page1_code, page1_data), (page2_code, page2_data), (page3_code, page3_data), ...)
      _Note_: If some error is returned during pagination (e.g. HTTP 401 at page 3), later pagination will be ignored.
    """
    if start<=0 or stop>0 and start>stop:
      raise InvalidArgumentsException("Invalid start(%s) and/or stop(%s)."%(start,stop))
    page = start
    rtn = []
    while True:
      tmp_params = params.copy()
      tmp_params["page"]=page
      tmp_params["per_page"]=DEFAULT_NUMBER_OF_ENTITIES_PER_PAGE
      code, data = req_method(url, tmp_params, headers)
      rtn.append((code,data))
      if code/100!=2:
        return tuple(rtn)
      elif page==stop:
        return tuple(rtn)
      len_data = len(json.loads(data))
      if len_data<DEFAULT_NUMBER_OF_ENTITIES_PER_PAGE or len_data==0:
        return tuple(rtn)
      else:
        page+=1

  def get_session(self, email, password):
    code, data = Session.get_session(self, email, password)
    if code/100==2:
      return json.loads(data)
    else:
      raise ApiException("Authentication failed with HTTP %s."%code)

  def get_users(self):
    rtn = []
    for code, data in Users.get_users(self):
      if code/100!=2:
        raise ApiException("Request failed with HTTP %s"%code)
      else:
        rtn += json.loads(data)
    return rtn

  def get_single_user(self, user_id):
    code, data = Users.get_single_user(self,user_id)
    if code/100==2:
      return json.loads(data)
    else:
      raise ApiException("Request failed with HTTP %s."%code)

  def create_user(self, email, password, username, name, skype=None, linkedin=None, twitter=None, project_limit=None, extern_uid=None, provider=None, bio=None):
    code, data = Users.create_user(self,email, password, username, name, skype, linkedin, twitter, project_limit, extern_uid, provider, bio)
    if code/100==2:
      return json.loads(data)
    else:
      raise ApiException("Operation failed with HTTP %s."%code)

  def modify_user(self, user_id, email=None, password=None, username=None, name=None, skype=None, linkedin=None, twitter=None, project_limit=None, extern_uid=None, provider=None, bio=None):
    code, data = Users.modify_user(self,user_id, email, password, username, name, skype, linkedin, twitter, project_limit, extern_uid, provider, bio)
    if code/100==2:
      return json.loads(data)
    elif code==405:
      raise ApiException("Operation failed with HTTP %s; 405 seems to me the problem on Nginx configuration with PUT method. Please google by yourself!"%code)
    else:
      raise ApiException("Operation failed with HTTP %s."%code)

  def delete_user(self, user_id):
    code, data = Users.delete_user(self, user_id)
    if code/100==2:
      return json.loads(data)
    elif code==405:
      raise ApiException("Operation failed with HTTP %s; 405 seems to me the problem on Nginx configuration with DELETE method. Please google by yourself!"%code)
    else:
      raise ApiException("Request failed with HTTP %s."%code)

  def get_current_user(self):
    code, data = Users.get_current_user(self)
    if code/100==2:
      return json.loads(data)
    else:
      raise ApiException("Request failed with HTTP %s."%code)

  def get_ssh_keys(self):
    rtn = []
    for code, data in Users.get_ssh_keys(self):
      if code/100!=2:
        raise ApiException("Request failed with HTTP %s"%code)
      else:
        rtn += json.loads(data)
    return rtn

  def get_single_ssh_key(self, key_id):
    code, data = Users.get_single_ssh_key(self,key_id)
    if code/100==2:
      return json.loads(data)
    else:
      raise ApiException("Request failed with HTTP %s."%code)

  def add_ssh_key(self, title, key):
    code, data = Users.add_ssh_key(self, title, key)
    if code/100==2:
      return json.loads(data)
    else:
      raise ApiException("Operation failed with HTTP %s."%code)

  def delete_ssh_key(self, key_id):
    code, data = Users.delete_ssh_key(self, key_id)
    if code/100==2:
      return json.loads(data)
    elif code==405:
      raise ApiException("Operation failed with HTTP %s; 405 seems to me the problem on Nginx configuration with DELETE method. Please google by yourself!"%code)
    else:
      raise ApiException("Request failed with HTTP %s."%code)
